from rest_framework import generics
from home.models import AxfUser,AxfCart,AxfGoods
from cart.cart_serializers import AddCartSerializer,ListCartSerializer
from rest_framework.response import Response
from cart.MyAuthentications import CartAuthentication
from cart.MyPermissions import CartPermission
from decimal import Decimal

# Create your views here.


class AddCartView(generics.CreateAPIView):
    '''
    post:
    闪送超市页添加购物车(每个商品右下角的button，黄色边框的空心圆，里面有一个红色边框的长方形，点击按钮将商品添加到购物车)。同时也是购物车页增加一种商品的数量(购物车页的加号)。
    '''
    queryset = AxfUser.objects.all()
    serializer_class = AddCartSerializer
    authentication_classes = (CartAuthentication,) # 局部认证，放在列表或元组(注意只有一个元素时末尾要加逗号)中，只对当前类(路由/接口)有效，但会使主urls.py中添加的登陆功能，在网页登录时失效(登录后右上角不显示已登录的用户名，但浏览器存了sessionid)，在URL中用@登录也失效
    permission_classes = (CartPermission,) # 局部权限管理，注意是元组，只有一个元素时末尾要加逗号

    def create(self, request, *args, **kwargs): # 同user/views.py中UserLoginView类中create()方法的注释
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            user=request.user # 通过认证后，认证类返回了user，可以从request中获取
            carts=AxfCart.objects.filter(c_user=user) # 当前商品(被点击的要添加到购物车的商品)在数据库中存在，当前用户在数据库中也存在，再查询当前用户的购物车
            goodsid=serializer.validated_data['goodsid']
            goods=AxfGoods.objects.get(pk=goodsid) # 查询当前商品
            carts=carts.filter(c_goods=goods) # 从当前用户的购物车中查询是否已有当前商品，已有的话将购物车中当前商品的数量加1，没有的话将当前商品添加到购物车
            if carts.exists():
                goods_in_cart=carts.first() # 从查询结果集中获取购物车中的当前商品。goodsid是唯一的，查到的carts中的数据是唯一的，所以用first()
                goods_in_cart.c_goods_num += 1
                goods_in_cart.save() # 将修改后的数据存入数据库
            else:
                goods_in_cart=AxfCart()
                goods_in_cart.c_goods_num=1
                goods_in_cart.c_is_select=1 # 添加到购物车后默认当前商品在购物车中已选中
                goods_in_cart.c_goods=goods
                goods_in_cart.c_user=user
                goods_in_cart.save()

            return Response({
                'code':200,
                'msg':'请求成功',
                'data':{'c_goods_num':goods_in_cart.c_goods_num}
            })
        else:
            return Response({
                'code':404,
                'msg':'该商品不存在', # token错误时无法通过认证，执行不到这里，所以报错提示中不用写token错误
                'data':{}
            })


class ListCartView(generics.ListAPIView):
    '''
    get:
    获取购物车页数据
    '''
    queryset = AxfCart.objects.all()
    serializer_class = ListCartSerializer
    authentication_classes = (CartAuthentication,)
    permission_classes = (CartPermission,)

    def list(self, request, *args, **kwargs): # 类似market/views.py中GoodsListView类中list()方法和user/views.py中UserLoginView类中create()方法的注释
        queryset=self.get_queryset()
        queryset=queryset.filter(c_user=request.user) # 自己的购物车，request.user是已登录的用户
        queryset=self.filter_queryset(queryset) # 因没定义过滤器类，所以这句写不写都行

        goods=queryset.all() # 购物车中的所有商品
        '''
        # 第一种写法
        c_is_select_list=[(goods_in_cart.c_goods_num,goods_in_cart.c_goods.price) for goods_in_cart in goods if goods_in_cart.c_is_select==1] # 类似当前文件中SelectAllOrNoneCartView类中partial_update()方法中的列表，写好那里后才完善的这里
        total_price=Decimal('0') # decimal模块的Decimal类型用于十进制浮点运算，参数类型是字符串时，计算结果和人工计算一致。from https://blog.csdn.net/yangjiajia123456/article/details/80302615和https://docs.python.org/zh-cn/3.6/tutorial/stdlib2.html#decimal-floating-point-arithmetic
        for i in c_is_select_list:
            total_price += Decimal(str(i[0])) * Decimal(str(i[1])) # 计算购物车已选择的商品的总价

        if len(c_is_select_list)==len(goods): # 购物车中已选择的商品数和购物车中的所有商品的数量相等，说明已全选。len(goods)也可以换成goods.count()，返回查询结果的总条数，即一共查到了几条数据
            is_all_select=True
        else:
            is_all_select=False
        '''
        # 第二种写法
        goods_is_select=goods.filter(c_is_select=1)
        total_price=Decimal('0')
        for goods_in_cart in goods_is_select:
            total_price += Decimal(str(goods_in_cart.c_goods_num)) * Decimal(str(goods_in_cart.c_goods.price))

        goods_not_select=goods.filter(c_is_select=0)
        if goods_not_select.exists():
            is_all_select=False
        else:
            is_all_select=True

        serializer = self.get_serializer(queryset, many=True) # 这一行和下一行的return是上面两种写法共用的
        return Response({
            'code':200,
            'msg':'请求成功',
            'data':{
                'title':'购物车',
                'is_all_select':is_all_select,
                'total_price':total_price,
                'carts':serializer.data
            }
        })


class SubCartView(generics.CreateAPIView): # 复制的当前文件的AddCartView类，做了一些修改
    '''
    post:
    购物车页减少一种商品的数量(购物车页的减号)
    '''
    queryset = AxfUser.objects.all()
    serializer_class = AddCartSerializer # 减少商品和增加商品可以共用一个序列化类
    authentication_classes = (CartAuthentication,)
    permission_classes = (CartPermission,)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            user = request.user
            carts = AxfCart.objects.filter(c_user=user)
            goodsid = serializer.validated_data['goodsid']
            goods = AxfGoods.objects.get(pk=goodsid)
            carts = carts.filter(c_goods=goods)
            if carts.exists():
                goods_in_cart = carts.first()
                # 从当前用户的购物车中查询当前商品的数量，大于1时将购物车中当前商品的数量减1，等于1或其他情况时从购物车中删除当前商品
                if goods_in_cart.c_goods_num > 1:
                    goods_in_cart.c_goods_num -= 1
                    goods_in_cart.save()
                else:
                    goods_in_cart.c_goods_num = 0 # 从购物车中删除后，不知道为什么返回时还能获取到数量(删除后数据库中购物车中确实没了当前商品，可能是执行delete()方法时没有立即删除，而是return后再删除)，所以这里手动将数量设为0。为了防止意外，将这一句写在delete()前
                    goods_in_cart.delete()
                return Response({
                    'code': 200,
                    'msg': '请求成功',
                    'data': {'c_goods_num': goods_in_cart.c_goods_num}
                })

        return Response({ # 购物车中没有当前商品，如用postman恶意提交
            'code': 404,
            'msg': '该商品不存在',
            'data': {}
        })


class IsSelectedCartView(generics.UpdateAPIView): # 参照的当前文件的AddCartView类
    '''
    patch:
    购物车页选择或取消选择一项商品
    '''
    queryset = AxfUser.objects.all()
    serializer_class = AddCartSerializer # 选择/取消选择可以和增加商品共用一个序列化类
    authentication_classes = (CartAuthentication,)
    permission_classes = (CartPermission,)

    def partial_update(self, request, *args, **kwargs): # 类似market/views.py中GoodsListView类中list()方法和user/views.py中UserLoginView类中create()方法的注释
        if 'id' in kwargs.keys():
            id=kwargs['id'] # URL中传入的参数和值(非GET请求，格式在urls.py中)，存在kwargs字典中，可从字典中获取；也可以将参数名直接写在形参中(需写在不定长参数前)，这时方法中可以直接用形参的值。当前文件夹中MyAuthentications.py中说request.data.get()可以获取非GET请求的参数的值，这里无法通过它获取id的值。
        else:
            id=None
        user = request.user
        carts = AxfCart.objects.filter(c_user=user)
        goods=carts.filter(pk=id) # 查询当前商品。用filter()不用get()的原因在当前文件夹中cart_serializers.py中AddCartSerializer类中validate_goodsid()方法中
        if goods.exists():
            goods_in_cart=goods.first()
            goods_in_cart.c_is_select=goods_in_cart.c_is_select^1 # AxfCart模型中c_is_select字段是整型，且值只有0或1，0表示当前商品在购物车中未选择，1表示当前商品在购物车中已选择。无论是0还是1，和1异或运算后都得到相反的值，即0变1，1变0，刚好符合选择/取消选择商品
            goods_in_cart.save()
            return Response({
                'code':200,
                'msg':'请求成功',
                'data':{}
            })

        return Response({ # 购物车中没有当前商品，如用postman恶意提交
            'code':404,
            'msg':'购物车中该商品不存在',
            'data':{}
        })


class SelectAllOrNoneCartView(generics.UpdateAPIView): # 参照的当前文件的IsSelectedCartView类
    '''
    patch:
    购物车页全选或取消全选
    '''
    queryset = AxfUser.objects.all()
    serializer_class = AddCartSerializer # 全选/取消全选可以和增加商品共用一个序列化类
    authentication_classes = (CartAuthentication,)
    permission_classes = (CartPermission,)

    def partial_update(self, request, *args, **kwargs):
        user = request.user
        carts = AxfCart.objects.filter(c_user=user)
        goods=carts.all() # 购物车中的所有商品
        '''
        # 第一种写法
        if goods.exists(): # 购物车中有商品
            c_is_select_list=[(goods_in_cart.pk,goods_in_cart.c_is_select) for goods_in_cart in goods if goods_in_cart.c_is_select==0] # 遍历购物车中的所有商品，若某个商品的c_is_select字段的值为0(表示未选择当前商品)，则将当前商品在购物车中的pk(即axf_cart表中的id)的值和当前商品的c_is_select字段的值作为元组放入列表，列表中每个元素都是元组
            if c_is_select_list: # 列表不为空，说明购物车中有未选择的商品(此时列表中每个元组的第二个元素为0)，此时'全选/取消全选'按钮的功能是全选，即将未选择的商品选择上
                for i in c_is_select_list: # 每个i都是元组
                    goods_in_cart=goods.filter(pk=i[0]).first() # 不加first()得到查询结果集，它没有save()方法，购物车中的pk是唯一的，所以查询结果集中的元素是唯一的，所以用first()
                    goods_in_cart.c_is_select=1
                    goods_in_cart.save()
            else: # 列表为空，说明购物车中已全选商品，此时'全选/取消全选'按钮的功能是'取消全选'
                for goods_in_cart in goods:
                    goods_in_cart.c_is_select=0
                    goods_in_cart.save()
            return Response({
                'code': 200,
                'msg': '请求成功',
                'data': {}
            })
        '''
        # 第二种写法
        if goods.exists():
            goods_not_select=goods.filter(c_is_select=0)
            if goods_not_select.exists(): # 说明购物车中有未选择的商品，此时'全选/取消全选'按钮的功能是全选，即将未选择的商品选择上
                for goods_in_cart in goods_not_select:
                    goods_in_cart.c_is_select=1
                    goods_in_cart.save()
            else: # 说明购物车中已全选商品，此时'全选/取消全选'按钮的功能是'取消全选'
                for goods_in_cart in goods: # 注意这里用的goods，不是goods_not_select
                    goods_in_cart.c_is_select=0
                    goods_in_cart.save()
            return Response({
                'code': 200,
                'msg': '请求成功',
                'data': {}
            })

        return Response({ # 购物车是空的。这是上面两种写法共用的
            'code':404,
            'msg':'购物车是空的',
            'data':{}
        })
